Skip to content

docs: Signal record components and DirtyState event - #419

Merged
jlucaso1 merged 3 commits into
mainfrom
claude/nifty-bohr-5qjctg
Jul 21, 2026
Merged

docs: Signal record components and DirtyState event#419
jlucaso1 merged 3 commits into
mainfrom
claude/nifty-bohr-5qjctg

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Documents the new public API surface added in oxidezap/whatsapp-rust#1062 ("feat(core): expose signal record components and dirty events"), merged to main today.

  • advanced/signal-protocol.mdx — new "Record components" section covering SessionRecordComponents/SenderKeyRecordComponents and the related component types, the from_components/into_components conversions on SessionRecord/SenderKeyRecord, import validation rules (sender-chain completeness, receiver-chain private-key rejection), export normalization (counter-lease advance before export, bounded archived-state/sender-key-state truncation), the new has_usable_sender_chain check, and the secret-redacting Debug output on all *Components types. Placed alongside the existing session/sender-key record content, cross-linking into the existing "Protocol safety limits" and "Flush scheduling" sections rather than duplicating them.
  • concepts/events.mdx — adds DirtyState to the Event enum listing and a new ### DirtyState subsection (modeled on the neighboring OfflineSyncCompleted entry) documenting when it fires (<ib><dirty type="..." timestamp="..."> from the server), its fields (dirty_type: DirtyType, timestamp: Option<u64>), the full DirtyType variant set, and that it's a pure observability hook — the client's built-in clean/resync IQ handling is unchanged.

No breaking changes were introduced by the source PR, so no existing doc content needed correction — this is purely additive.

Test plan

  • Verified both files' pushed content is byte-identical to the drafted source (diffed before push)
  • mint broken-links / local preview (not run in this session — no local Mintlify environment)

🤖 Generated with Claude Code


Generated by Claude Code


Summary by cubic

Adds docs for the Signal record components API and the DirtyState event. Docs-only; clarifies validation, normalized import/export rules, and event ordering with no runtime changes.

  • New Features
    • advanced/signal-protocol.mdx: New “Record components” section documenting SessionRecordComponents/SenderKeyRecordComponents, from_components/into_components, import validation (complete sender chain; reject receiver-chain private keys; accept raw or canonical pubkeys and export canonical), export normalization (advance counter leases, drop sender chains too stale to advance, bounded truncation), has_usable_sender_chain, and redacted Debug.
    • concepts/events.mdx: Adds DirtyState to Event, including when it fires (<ib><dirty ...>), that it fires before built-in clean/resync, fields (dirty_type, timestamp), full DirtyType variants (AccountSync, Groups, SyncdAppState, NewsletterMetadata, Other(String)), and that it’s an observability hook only.

Written for commit 9cb05e1. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added documented record-component APIs for converting session and sender-key records to and from validated, structured data.
    • Added session helpers to identify whether a usable sender chain is available.
    • Added a documented DirtyState event with its type and optional timestamp.
  • Documentation

    • Documented validation, normalization, safe state handling, and secret-redacted debug output for record components.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The documentation adds Signal record component APIs and validation semantics, and documents a new DirtyState event variant with its payload and relationship to existing clean/resync handling.

Changes

Signal record components

Layer / File(s) Summary
Record components API
advanced/signal-protocol.mdx
Documents session and sender-key component shapes, conversion APIs, import validation, export normalization, sender-chain usability checks, and redacted Debug output.

DirtyState event

Layer / File(s) Summary
DirtyState event reference
concepts/events.mdx
Adds DirtyState(DirtyState) to the event list and documents its dirty_type, optional timestamp, server marker, and observability-only behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

A rabbit found records neatly split,
With secret crumbs concealed in wit.
A dirty bell rang through the night,
Its timestamp tucked away just right.
“The clean path still runs,” I cheer—
“New docs are hopping into gear!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the additive docs changes for Signal record components and the new DirtyState event.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mintlify

mintlify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
whatsapp-rust 🟢 Ready View Preview Jul 21, 2026, 2:55 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR documents new Signal record APIs and dirty-state events. The main changes are:

  • Adds validated component shapes and conversions for session and sender-key records.
  • Explains import checks, export normalization, sender-chain usability, and secret redaction.
  • Documents the DirtyState event, its fields, variants, timing, and built-in handling.

Confidence Score: 5/5

This looks safe to merge.

  • The revised text clearly explains that component conversion can normalize or remove state.
  • No blocking issues remain in the changed documentation.

Important Files Changed

Filename Overview
advanced/signal-protocol.mdx Documents Signal record components and now clearly describes conversion as normalized re-encoding rather than a lossless round trip.
concepts/events.mdx Documents the new DirtyState event and its relationship to existing clean and resync behavior.

Reviews (2): Last reviewed commit: "docs: address review feedback on record-..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@advanced/signal-protocol.mdx`:
- Line 1238: Rewrite the dense introductory sentence around the SessionRecord
and SenderKeyRecord components into several concise sentences, separating the
caller use cases, the definition of components, the additive nature of the API,
and the canonical round-trip guarantee. Preserve all existing technical details
and the serialize()/deserialize() behavior.

In `@concepts/events.mdx`:
- Around line 2000-2017: Revise the DirtyState documentation to use concise,
single-idea sentences and active, second-person wording. Split the Emitted
description and the Fields explanations as needed, replacing third-person
references such as “the client” or “a handler” with “you” where appropriate.
Preserve the existing protocol behavior, event ordering, and RawNode guidance
while simplifying the long Note into shorter statements.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b80ab62e-f4a1-48ff-a4bc-97e1b97223ca

📥 Commits

Reviewing files that changed from the base of the PR and between 198f79e and fd9a30a.

📒 Files selected for processing (2)
  • advanced/signal-protocol.mdx
  • concepts/events.mdx

Comment thread advanced/signal-protocol.mdx Outdated

## Record components

For callers that need to interchange or inspect session and sender-key record state without depending on the generated protobuf schema directly — custom store implementations, migration tooling, offline debugging — `wacore-libsignal` exposes owned, validated projections of `SessionRecord` and `SenderKeyRecord` called **components**. This is purely additive: the protobuf-backed `serialize()`/`deserialize()` path is unchanged, and a canonical record round-trips losslessly through `into_components()` → `from_components()` → `serialize()`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Split this dense sentence for readability.

This single sentence packs the use cases, the "components" definition, the additive guarantee, and the round-trip chain together. Break it into a few sentences (one idea each) to match the docs style.

As per coding guidelines: "Keep sentences concise — one idea per sentence in documentation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@advanced/signal-protocol.mdx` at line 1238, Rewrite the dense introductory
sentence around the SessionRecord and SenderKeyRecord components into several
concise sentences, separating the caller use cases, the definition of
components, the additive nature of the API, and the canonical round-trip
guarantee. Preserve all existing technical details and the
serialize()/deserialize() behavior.

Source: Coding guidelines

Comment thread concepts/events.mdx
Comment on lines +2000 to +2017
**Emitted:** When the server sends an `<ib><dirty type="..." timestamp="...">` marker, telling the client one of its cached protocol domains is stale server-side.

```rust
#[derive(Debug, Clone, Serialize, bon::Builder)]
#[non_exhaustive]
pub struct DirtyState {
pub dirty_type: DirtyType,
pub timestamp: Option<u64>,
}
```

**Fields:**
- `dirty_type` - The stale domain, mirroring `wacore::iq::dirty::DirtyType`: `AccountSync`, `Groups`, `SyncdAppState`, `NewsletterMetadata`, or `Other(String)` for a wire value the client doesn't otherwise recognize.
- `timestamp` - `Option<u64>`, `None` if the `<dirty>` stanza omitted the `timestamp` attribute.

<Note>
This is a pure observability hook — it does not replace or gate the client's built-in handling. The client always sends the matching `<clean>` IQ (throttled behind offline-sync completion for `Groups`/`NewsletterMetadata`, per `WAWebHandleDirtyBits`) and, for `SyncdAppState`, re-syncs all app-state collections, exactly as it did before this event existed. `DirtyState` fires first, right before that built-in work starts, so a handler can refresh its own domain-specific derived state (e.g. invalidate a local groups cache) without parsing raw `<ib>` stanzas via [`RawNode`](#raw-stanza-events) or racing the client's own resync.
</Note>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Apply documentation guidelines for conciseness and perspective.

The current description contains long, complex sentences and uses the third person (e.g., "telling the client", "so a handler can"). As per coding guidelines, documentation in .mdx files must use the active voice, the second person ("you"), and keep sentences concise (one idea per sentence).

📝 Proposed refactor for documentation clarity
-**Emitted:** When the server sends an `<ib><dirty type="..." timestamp="...">` marker, telling the client one of its cached protocol domains is stale server-side.
+**Emitted:** When the server sends an `<ib><dirty type="..." timestamp="...">` marker. This tells your client that a cached protocol domain is stale on the server.
 
 ```rust
 #[derive(Debug, Clone, Serialize, bon::Builder)]
 #[non_exhaustive]
 pub struct DirtyState {
     pub dirty_type: DirtyType,
     pub timestamp: Option<u64>,
 }

Fields:
-- dirty_type - The stale domain, mirroring wacore::iq::dirty::DirtyType: AccountSync, Groups, SyncdAppState, NewsletterMetadata, or Other(String) for a wire value the client doesn't otherwise recognize.
-- timestamp - Option<u64>, None if the <dirty> stanza omitted the timestamp attribute.
+- dirty_type - The stale domain. This mirrors wacore::iq::dirty::DirtyType: AccountSync, Groups, SyncdAppState, NewsletterMetadata, or Other(String) for a wire value your client doesn't otherwise recognize.
+- timestamp - An Option<u64>. This is None if the <dirty> stanza omitted the timestamp attribute.

-This is a pure observability hook — it does not replace or gate the client's built-in handling. The client always sends the matching `` IQ (throttled behind offline-sync completion for `Groups`/`NewsletterMetadata`, per `WAWebHandleDirtyBits`) and, for `SyncdAppState`, re-syncs all app-state collections, exactly as it did before this event existed. `DirtyState` fires first, right before that built-in work starts, so a handler can refresh its own domain-specific derived state (e.g. invalidate a local groups cache) without parsing raw `` stanzas via [`RawNode`](`#raw-stanza-events`) or racing the client's own resync. +This is a pure observability hook. It does not replace or gate the client's built-in handling. The client always sends the matching `` IQ. This is throttled behind offline-sync completion for `Groups` and `NewsletterMetadata` (per `WAWebHandleDirtyBits`). For `SyncdAppState`, the client re-syncs all app-state collections exactly as it did before this event existed. `DirtyState` fires first, right before that built-in work starts. This allows you to refresh your domain-specific derived state (e.g., invalidate a local groups cache). You do not need to parse raw `` stanzas via [`RawNode`](`#raw-stanza-events`) or race the client's own resync. ```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
**Emitted:** When the server sends an `<ib><dirty type="..." timestamp="...">` marker, telling the client one of its cached protocol domains is stale server-side.
```rust
#[derive(Debug, Clone, Serialize, bon::Builder)]
#[non_exhaustive]
pub struct DirtyState {
pub dirty_type: DirtyType,
pub timestamp: Option<u64>,
}
```
**Fields:**
- `dirty_type` - The stale domain, mirroring `wacore::iq::dirty::DirtyType`: `AccountSync`, `Groups`, `SyncdAppState`, `NewsletterMetadata`, or `Other(String)` for a wire value the client doesn't otherwise recognize.
- `timestamp` - `Option<u64>`, `None` if the `<dirty>` stanza omitted the `timestamp` attribute.
<Note>
This is a pure observability hook — it does not replace or gate the client's built-in handling. The client always sends the matching `<clean>` IQ (throttled behind offline-sync completion for `Groups`/`NewsletterMetadata`, per `WAWebHandleDirtyBits`) and, for `SyncdAppState`, re-syncs all app-state collections, exactly as it did before this event existed. `DirtyState` fires first, right before that built-in work starts, so a handler can refresh its own domain-specific derived state (e.g. invalidate a local groups cache) without parsing raw `<ib>` stanzas via [`RawNode`](#raw-stanza-events) or racing the client's own resync.
</Note>
**Emitted:** When the server sends an `<ib><dirty type="..." timestamp="...">` marker. This tells your client that a cached protocol domain is stale on the server.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@concepts/events.mdx` around lines 2000 - 2017, Revise the DirtyState
documentation to use concise, single-idea sentences and active, second-person
wording. Split the Emitted description and the Fields explanations as needed,
replacing third-person references such as “the client” or “a handler” with “you”
where appropriate. Preserve the existing protocol behavior, event ordering, and
RawNode guidance while simplifying the long Note into shorter statements.

Source: Coding guidelines

Comment thread advanced/signal-protocol.mdx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fd9a30aed0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread advanced/signal-protocol.mdx Outdated

## Record components

For callers that need to interchange or inspect session and sender-key record state without depending on the generated protobuf schema directly — custom store implementations, migration tooling, offline debugging — `wacore-libsignal` exposes owned, validated projections of `SessionRecord` and `SenderKeyRecord` called **components**. This is purely additive: the protobuf-backed `serialize()`/`deserialize()` path is unchanged, and a canonical record round-trips losslessly through `into_components()` → `from_components()` → `serialize()`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid promising a lossless component round trip

The lossless-round-trip claim does not hold for canonical records containing an active counter lease: as documented below, into_components() advances the chain to the lease ceiling and can drop a chain that cannot be advanced, so reconstructing and serializing the record does not preserve its original state or bytes. This matters especially for the custom-store and migration callers introduced here; describe the conversion as a safe normalized projection instead of a lossless round trip.

Useful? React with 👍 / 👎.

Comment thread concepts/events.mdx
- `timestamp` - `Option<u64>`, `None` if the `<dirty>` stanza omitted the `timestamp` attribute.

<Note>
This is a pure observability hook — it does not replace or gate the client's built-in handling. The client always sends the matching `<clean>` IQ (throttled behind offline-sync completion for `Groups`/`NewsletterMetadata`, per `WAWebHandleDirtyBits`) and, for `SyncdAppState`, re-syncs all app-state collections, exactly as it did before this event existed. `DirtyState` fires first, right before that built-in work starts, so a handler can refresh its own domain-specific derived state (e.g. invalidate a local groups cache) without parsing raw `<ib>` stanzas via [`RawNode`](#raw-stanza-events) or racing the client's own resync.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not promise that handlers run before the built-in resync

This ordering does not prevent a race for the default Bot API: EventDelivery::Concurrent spawns closure callbacks on separate tasks, so dispatch returns and the built-in clean/resync work can start before a DirtyState callback runs or finishes. Only an inline struct-based EventHandler executes before dispatch returns, so the note should not broadly promise that handlers can refresh derived state without racing the client's resync.

Useful? React with 👍 / 👎.

@jlucaso1
jlucaso1 merged commit 59e0f29 into main Jul 21, 2026
2 of 3 checks passed
@mintlify

mintlify Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
whatsapp-rust 🟡 Building Jul 21, 2026, 2:54 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant